package nachos.threads;

import nachos.machine.*;

/**
 * Lock 是一种同步术语，它有两种状态：busy 和 free
 * 一个锁上只允许两种操作：
 *
 * acquire(): 自动等待，直到锁 free，然后将其设置为 busy
 * release(): 将锁设置为空闲，如果可能，唤醒一个等待的线程
 *
 * <p>
 * 此外，只有获得锁的线程才能释放它
 * 与信号量一样，API 不允许您读取锁状态（因为读取后，该值可能会立即更改）
 */
public class Lock {
    /**
     * 分配一个新的锁，锁最初的状态是 free
     */
    public Lock() {
    }

    /**
     * 自动获取此锁
     * 当前线程不能已经持有此锁
     */
    public void acquire() {
        Lib.assertTrue(!isHeldByCurrentThread());

        boolean intStatus = Machine.interrupt().disable();
        KThread thread = KThread.currentThread();

        if (lockHolder != null) {
            waitQueue.waitForAccess(thread);
            KThread.sleep();
        } else {
            waitQueue.acquire(thread);
            lockHolder = thread;
        }

        Lib.assertTrue(lockHolder == thread);

        Machine.interrupt().restore(intStatus);
    }

    /**
     * 自动释放此锁，允许其他线程获取它
     */
    public void release() {
        Lib.assertTrue(isHeldByCurrentThread());

        boolean intStatus = Machine.interrupt().disable();

        if ((lockHolder = waitQueue.nextThread()) != null)
            lockHolder.ready();

        Machine.interrupt().restore(intStatus);
    }

    /**
     * 测试当前线程是否持有此锁
     *
     * @return true 如果当前线程持有此锁
     */
    public boolean isHeldByCurrentThread() {
        return (lockHolder == KThread.currentThread());
    }

    private KThread lockHolder = null;
    private ThreadQueue waitQueue =
            ThreadedKernel.scheduler.newThreadQueue(true);
}
